package taskservice

import (
	"errors"
	"fmt"
	"math"
	"net/http"
	"path/filepath"
	"time"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/model"
	"yunj/pkg/yunj/app/service/admin/memberservice"
	"yunj/pkg/yunj/core/builder/builderconsts"
	"yunj/pkg/yunj/core/builder/builderdto"
	"yunj/pkg/yunj/util"

	"github.com/valyala/fasthttp"
	"github.com/xuri/excelize/v2"
)

// 表格构建器导出
type TableBuilderExport struct {
	task             *model.AdminTask              // 任务模型
	taskRequestParam *builderdto.TableRequestParam // 过滤参数
	taskUserAgent    string
	totalCount       uint64 // 总数量
	isSetTotalCount  bool   // 是否设置总数量
	excelFile        *excelize.File
}

func NewTableBuilderExport() *TableBuilderExport {
	return &TableBuilderExport{}
}

// 设置任务模型
func (t *TableBuilderExport) SetTask(task *model.AdminTask) {
	t.task = task
}

// 获取任务模型
func (t *TableBuilderExport) GetTask() *model.AdminTask {
	return t.task
}

// 执行任务
func (t *TableBuilderExport) Handle() (err error) {
	task := t.GetTask()
	var rate uint8
	// 开始处理
	if err = task.ToProcessing(rate); err != nil {
		return
	}
	requestParam := t.GetTaskRequestParam()
	if requestParam == nil || requestParam.Export == nil {
		err = errors.New("异常请求任务！")
		return
	}
	// 创建新的Excel文件
	t.excelFile = excelize.NewFile()
	defer t.excelFile.Close()
	// 表头数据写入文件
	t.headerDataToFile(requestParam)

	apiUrl := requestParam.Export.ApiUrl
	batchSize := builderconsts.TABLE_BUILDER_EXPORT_API_SINGLE_LIMIT
	var itemsTotal uint64
	currPage := 1
	apiRequestParam := *requestParam
	apiRequestParam.BuilderAsyncType = "items"
	apiRequestParam.Export = nil
	token := ""
	for {
		// 获取访问token
		if token = t.GetMemberToken(token); token == "" {
			err = errors.New("获取访问token失败！")
			return
		}
		// 获取items数据
		apiRequestParam.Page = currPage
		apiRequestParam.PageSize = batchSize
		var data TableBuilderExportApiItemsData
		data, err = handleTableBuilderExportApi[TableBuilderExportApiItemsData](apiUrl, apiRequestParam, token, t.GetTaskUserAgent())
		if err != nil {
			err = fmt.Errorf("获取page=%d的items数据异常！%v", currPage, err)
			return
		}
		// fmt.Printf("123123|%v\r\n", data)
		itemsLen := len(data.Items)
		if itemsLen == 0 {
			break
		}
		// 添加items数据
		err = t.itemsDataToFile(&data, itemsTotal)
		if err != nil {
			err = fmt.Errorf("items数据写入文件异常！%v", err)
			return
		}
		// 记录总数据行
		itemsTotal += uint64(itemsLen)
		// 没有启用分页，或者当前页数量小于当前批次请求数量则结束循环
		if !requestParam.Export.IsEnablePage || itemsLen < batchSize {
			break
		}
		// 获取进度
		rate, err = t.getRate(requestParam, itemsTotal)
		if err != nil {
			err = fmt.Errorf("获取进度异常！%v", err)
			return
		}
		// 设置进度
		task.ToProcessing(rate)
		// 下一页
		currPage++
	}

	// 保存文件
	outputFile, err := t.saveExcelFile(requestParam)
	if err != nil {
		err = fmt.Errorf("保存文件异常！%v", err)
		return
	}
	task.Snap.SetAttr("output_file", outputFile)
	// 处理完成
	HandleComplated(task)
	return
}

// 获取进度。仅针对启用分页的导出有效
func (t *TableBuilderExport) getRate(requestParam *builderdto.TableRequestParam, itemsTotal uint64) (rate uint8, err error) {
	if !requestParam.Export.IsEnablePage {
		return
	}
	// 获取总的数据量
	totalCount, err := t.getTotalCount(requestParam)
	if err != nil {
		return
	}
	// 获取当前进度
	if itemsTotal >= totalCount {
		rate = 99
		return
	}
	rate = uint8(math.Floor((float64(itemsTotal) / float64(totalCount)) * 100))
	return
}

// 获取总的数据量
func (t *TableBuilderExport) getTotalCount(requestParam *builderdto.TableRequestParam) (totalCount uint64, err error) {
	if t.isSetTotalCount {
		totalCount = t.totalCount
		return
	}
	apiUrl := requestParam.Export.ApiUrl
	apiRequestParam := *requestParam
	apiRequestParam.BuilderAsyncType = "count"
	apiRequestParam.Export = nil
	// 获取访问token
	token := t.GetMemberToken()
	if token == "" {
		err = errors.New("查询导出数据总量，获取访问token失败！")
		return
	}
	data, err := handleTableBuilderExportApi[TableBuilderExportApiCountData](apiUrl, apiRequestParam, token, t.GetTaskUserAgent())
	if err != nil {
		err = fmt.Errorf("查询导出数据总量异常！%v", err)
		return
	}
	totalCount = data.Count
	t.totalCount = totalCount
	t.isSetTotalCount = true
	return
}

// 表头数据写入文件
func (t *TableBuilderExport) headerDataToFile(requestParam *builderdto.TableRequestParam) {
	for col, title := range requestParam.Export.FieldTitles {
		cell, _ := excelize.CoordinatesToCellName(col+1, 1)
		t.excelFile.SetCellValue("Sheet1", cell, title)
	}
}

// 保存excel文件
func (t *TableBuilderExport) saveExcelFile(requestParam *builderdto.TableRequestParam) (outputFile string, err error) {
	saveDir := "storage/upload/export"
	now := time.Now()
	datePath := filepath.Join(
		fmt.Sprintf("%d", now.Year()),
		fmt.Sprintf("%02d", now.Month()),
		fmt.Sprintf("%02d", now.Day()),
	)
	fullPath := filepath.Join(saveDir, datePath)
	// 确保目录存在，不存在则创建
	if err = util.EnsureDir(fullPath); err != nil {
		err = fmt.Errorf("创建保存目录失败: %v", err)
		return
	}
	outputFile = filepath.Join(fullPath, fmt.Sprintf("%s_%s.%s", requestParam.Export.Filename, now.Format("20060102150405"), requestParam.Export.FileExt))
	if err = t.excelFile.SaveAs(outputFile); err != nil {
		err = fmt.Errorf("保存文件失败: %v", err)
	}
	return
}

// 将items数据写入文件
func (t *TableBuilderExport) itemsDataToFile(data *TableBuilderExportApiItemsData, itemsTotal uint64) (err error) {
	for y, item := range data.Items {
		for x, field := range t.taskRequestParam.Export.Fields {
			cell, _ := excelize.CoordinatesToCellName(x+1, int(itemsTotal)+2+y)
			value := item[field]
			if value != nil {
				if !util.IsComparable(value) {
					if valueJson, err := util.ToJson(value); err == nil {
						value = valueJson
					}
				}
			}
			t.excelFile.SetCellValue("Sheet1", cell, value)
		}
	}
	return
}

// 获取任务过滤参数
func (t *TableBuilderExport) GetTaskRequestParam() *builderdto.TableRequestParam {
	if t.taskRequestParam != nil {
		return t.taskRequestParam
	}
	task := t.GetTask()
	requestParamRaw := task.Snap.GetAttr("request_param")
	if _requestParam, ok := requestParamRaw.(*builderdto.TableRequestParam); ok {
		t.taskRequestParam = _requestParam
		return t.taskRequestParam
	}

	var requestParamJson string
	if requestParamMap, ok := requestParamRaw.(map[string]any); ok {
		requestParamJson, _ = util.ToJson(requestParamMap)
	} else if resStr, ok := requestParamRaw.(string); ok {
		requestParamJson = resStr
	}
	if requestParamJson != "" {
		_requestParam, err := util.JsonTo[*builderdto.TableRequestParam](requestParamJson)
		if err != nil {
			err = fmt.Errorf("任务 %s[%d] 快照过滤参数类型错误！%v", task.Title, task.Id, err)
			global.Logger.WithField("task", task).Error(err)
		} else {
			t.taskRequestParam = _requestParam
			return t.taskRequestParam
		}
	}
	return nil
}

// 获取成员token
func (t *TableBuilderExport) GetTaskUserAgent() string {
	if t.taskUserAgent == "" {
		task := t.GetTask()
		t.taskUserAgent = fmt.Sprintf("%s Task:%d Member:%d", builderconsts.TABLE_EXPORT_USER_AGENT_PREFIX, task.Id, task.MemberId)
	}
	return t.taskUserAgent
}

// 获取成员token
func (t *TableBuilderExport) GetMemberToken(token ...string) string {
	var adminMember *model.AdminMember
	if len(token) > 0 && token[0] != "" {
		adminMember = memberservice.VerifyToken(token[0], t.GetTaskUserAgent(), true, 60)
		if adminMember != nil {
			return token[0]
		}
	}
	adminMember = memberservice.GetMemberById(t.GetTask().MemberId)
	return memberservice.CreateToken(adminMember, t.GetTaskUserAgent(), 60)

}

// API响应结果类型
type TableBuilderExportApiResult[DT any] struct {
	Errcode int    `json:"errcode"`
	Msg     string `json:"msg"`
	Data    DT     `json:"data"`
}

// 数据项请求数据项响应数据
type TableBuilderExportApiItemsData struct {
	Items []map[string]any `json:"items"`
}

// 数据项请求数量响应数据
type TableBuilderExportApiCountData struct {
	Count uint64 `json:"count"`
}

// 执行导出任务api请求
func handleTableBuilderExportApi[DT any](url string, param builderdto.TableRequestParam, token, userAgent string) (data DT, err error) {
	// 序列化为JSON
	paramJson, err := util.ToJson(param)
	if err != nil {
		err = fmt.Errorf("API数据请求参数JSON序列化失败: %v", err)
		return
	}
	// 创建fasthttp客户端
	client := &fasthttp.Client{}
	// 创建请求对象
	req := fasthttp.AcquireRequest()
	req.SetRequestURI(url)
	req.Header.SetMethod(fasthttp.MethodPost)
	req.Header.SetContentType("application/json")
	req.Header.Set("token", token)
	req.Header.Set("User-Agent", userAgent)
	req.SetBodyString(paramJson)
	// 创建响应对象
	resp := fasthttp.AcquireResponse()
	err = client.DoTimeout(req, resp, 60*time.Second)
	fasthttp.ReleaseRequest(req)
	defer fasthttp.ReleaseResponse(resp)
	if err != nil {
		err = fmt.Errorf("API数据请求异常！%v", err)
		return
	}
	// 检查响应状态码
	statusCode := resp.StatusCode()
	respBody := resp.Body()
	if statusCode != http.StatusOK {
		err = fmt.Errorf("API数据请求失败，状态码: %d, 响应: %s", statusCode, respBody)
		return
	}
	apiRes, err := util.JsonTo[TableBuilderExportApiResult[DT]](respBody)
	if err != nil {
		err = fmt.Errorf("API数据响应转换失败，%v，响应：%s", err, respBody)
	}
	if apiRes.Errcode != 0 {
		err = fmt.Errorf("API数据请求失败，错误码: %d, 错误信息: %s", apiRes.Errcode, apiRes.Msg)
	}
	data = apiRes.Data
	return
}
